home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 June: Reference Library / Dev.CD Jun 96 RL / Dev.CD Jun 96 RL.toast / What's New? / Development Kits / Apple Game Sprockets DR1 / Examples / GlyphaIII / G3Play.c < prev    next >
Encoding:
Text File  |  1996-04-24  |  46.2 KB  |  1,346 lines  |  [TEXT/MPS ]

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Play.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // This (rather large) file handles all player routines while a game is in…
  9. // progress. It gets the player's input, moves the player, tests for collisions…
  10. // and generally handles the "main game loop".  Enemies and actually drawing…
  11. // the graphics to the screen are handled in other files.
  12.  
  13. #include "G3Externs.h"
  14.  
  15. #if GENERATINGPOWERPC
  16.     #include "InputSprocket.h"
  17. #endif
  18.  
  19.  
  20. #define kFlapImpulse            48
  21. #define kGlideImpulse            12
  22. #define kAirResistance            2
  23. #define kMaxHVelocity            192
  24. #define kMaxVVelocity            512
  25. #define kNumLightningStrikes    5
  26.  
  27.  
  28. void SetUpLevel (void);
  29. void ResetPlayer (Boolean);
  30. void OffAMortal (void);
  31. void DoCommandKey (void);
  32. void GetPlayerInput (void);
  33. void HandlePlayerIdle (void);
  34. void HandlePlayerFlying (void);
  35. void HandlePlayerWalking (void);
  36. void HandlePlayerSinking (void);
  37. void HandlePlayerFalling (void);
  38. void HandlePlayerBones (void);
  39. void MovePlayer (void);
  40. void CheckTouchDownCollision (void);
  41. void CheckPlatformCollision (void);
  42. void KeepPlayerOnPlatform (void);
  43. void CheckLavaRoofCollision (void);
  44. void SetAndCheckPlayerDest (void);
  45. void HandleLightning (void);
  46. void FinishLightning (void);
  47. void HandleCountDownTimer (void);
  48. void CheckHighScore (void);
  49.  
  50.  
  51. playerType    thePlayer;
  52. enemyType    theEnemies[kMaxEnemies];
  53. KeyMap        theKeys;
  54. Rect        platformRects[6], touchDownRects[6], enemyRects[24];
  55. Rect        enemyInitRects[5];
  56. long        theScore, wasTensOfThousands;
  57. short        numLedges, beginOnLevel, levelOn, livesLeft, lightH, lightV;
  58. short        lightningCount, numEnemies, countDownTimer;
  59. Boolean        playing, pausing, flapKeyDown, evenFrame;
  60. Boolean        doEnemyFlapSound, doEnemyScrapeSound;
  61.  
  62. extern    handInfo    theHand;
  63. extern    prefsInfo    thePrefs;
  64. extern    Rect        playerRects[11], mainWindowRect;
  65. extern    short        numUpdateRects1, numUpdateRects2, numOwls;
  66. extern    Boolean        quitting, openTheScores;
  67.  
  68. //
  69.  
  70. #if GENERATINGPOWERPC
  71. OSErr EasyJoystick(Boolean *left, Boolean *right, Boolean *button);
  72. OSErr EasyJoystick(Boolean *left, Boolean *right, Boolean *button)
  73. {
  74.     // remember a little across calls
  75.     static ElementReference lastXAxis = 0;
  76.     static Boolean lastLeft = false;
  77.     static Boolean lastRight = false;
  78.     static Boolean lastButton = false;
  79.  
  80.     int maxEvents = 10;        // process only ten events per round
  81.     
  82.     ElementEvent theEvent;            // our event
  83.     Boolean wasEvent = true;
  84.     OSErr err = noErr;
  85.     ElementListReference globalList;
  86.     ElementInfo info;
  87.     
  88.     *left = lastLeft;
  89.     *right = lastRight;
  90.     *button = lastButton;
  91.  
  92.     err = GetGlobalElementList(&globalList);
  93.  
  94.     while(maxEvents > 0)
  95.     {
  96.         err = ElementListGetNextEvent(globalList, sizeof(theEvent), &theEvent, &wasEvent);
  97.         
  98.         if ((wasEvent == false) || (err))
  99.         {
  100.             break;
  101.         }
  102.         
  103.         GetElementInfo(theEvent.element, &info);
  104.  
  105.         // get information about this element since we are not doing any configuration
  106.         // we need to know the about labels and kinds
  107.  
  108.         switch(info.theKind)
  109.         {
  110.             case kElementKindButton:
  111.             {
  112.                 Boolean newValue;
  113.                 
  114.                 // figure out the new value for the boolean
  115.                 if (theEvent.data)
  116.                 {
  117.                     newValue = true;
  118.                 }
  119.                 else
  120.                 {
  121.                     newValue = false;
  122.                 }
  123.     
  124.                 *button = newValue;
  125.             }
  126.             break;
  127.             
  128.             case kElementKindDPad:
  129.             {
  130.                 if (info.theLabel == kElementLabelPadMove)
  131.                 {
  132.                     // a movement DPad so they might be using
  133.                     // a console style direction pad so turn
  134.                     // that into axis style data
  135.                     
  136.                     lastXAxis = nil;
  137.                     
  138.                     switch(theEvent.data)
  139.                     {
  140.                         case kPadIdle:
  141.                         case kPadUp:
  142.                         case kPadDown:            
  143.                             *left = false;
  144.                             *right = false;
  145.                             break;
  146.                         case kPadLeft:
  147.                         case kPadUpLeft:
  148.                         case kPadDownLeft:
  149.                             *left = true;
  150.                             *right = false;
  151.                             break;
  152.                         case kPadUpRight:
  153.                         case kPadRight:
  154.                         case kPadDownRight:
  155.                             *left = false;
  156.                             *right = true;
  157.                             break;
  158.                     }
  159.                 }
  160.             }
  161.             break;
  162.             
  163.             case kElementKindAxis:
  164.             {
  165.                 // if it is an axis find out if it is the
  166.                 // x or y axis style data and use that.
  167.                 
  168.                 if (info.theLabel == kElementLabelXAxis)
  169.                 {
  170.                     lastXAxis = theEvent.element;
  171.                 }
  172.                 
  173.                 ElementFlush(theEvent.element);
  174.             }
  175.             break;
  176.         }
  177.         
  178.         maxEvents--;
  179.     }
  180.     
  181.     // poll the last meaningful axis we had on the way out
  182.     // just to make sure we had good data
  183.     if (lastXAxis != nil)
  184.     {
  185.         SInt32 xAxis;
  186.         
  187.         *left = false;
  188.         *right = false;
  189.     
  190.         GetElementSimpleState(lastXAxis, &xAxis);
  191.  
  192.         if (xAxis > (500000000))
  193.         {
  194.             *right = true;
  195.         }
  196.         else if (xAxis < (-500000000))
  197.         {
  198.             *left = true;
  199.         }
  200.     }
  201.     
  202.     return err;
  203. }
  204. #endif
  205.  
  206. //==============================================================  Functions
  207. //--------------------------------------------------------------  InitNewGame
  208.  
  209. // This funciton sets up variables and readies for a new game.  It is called…
  210. // only when a the user selects "New Game" - during the course of a game, it…
  211. // is  not called again.
  212.  
  213. void InitNewGame (void)
  214. {                                // Initialize a number of game variables.
  215.     countDownTimer = 0;            // Zero count down timer.
  216.     numLedges = 3;                // Initial number of ledges (platforms).
  217.     beginOnLevel = 1;            // Ledge (platform) the player is on (center ledge).
  218.     levelOn = 0;                // Game level on (first level).
  219.     livesLeft = kInitNumLives;    // Number of player lives remaining.
  220.     theScore = 0L;                // Player's score (a long - can go to 2 billion).
  221.     playing = TRUE;                // Flag playing.
  222.     pausing = FALSE;            // Not paused.
  223.     evenFrame = TRUE;            // Set an initial state for evenFrame.
  224.     wasTensOfThousands = 0L;    // Used for noting when player gets an extra life.
  225.     numOwls = 4;                // Number of "owl" enemies for this level.
  226.     
  227.     numUpdateRects1 = 0;        // Init number of "update" rectangles.
  228.     numUpdateRects2 = 0;        // (see Render.c to see what these do)
  229.     
  230.     InitHandLocation();            // Get the mummy hand down in the lava.
  231.     theHand.mode = kLurking;    // Flag the hand in "lurking" mode.
  232.     
  233.     SetUpLevel();                // Set up platforms for first level (wave).
  234.     
  235.     DumpBackToWorkMap();        // Copy background offscreen to "work" offscreen.
  236.     
  237.     UpdateLivesNumbers();        // Display number of lives remaining on screen.
  238.     UpdateScoreNumbers();        // Display the player's score (zero at this point).
  239.     UpdateLevelNumbers();        // Display the level (wave) the player is on.
  240.     
  241.     GenerateEnemies();            // Prepare all enemies for this level.
  242.     ResetPlayer(TRUE);            // Initialize all player variables and put on ledge.
  243. }
  244.  
  245. //--------------------------------------------------------------  SetUpLevel
  246.  
  247. // Primarily, this function is called to set up the ledges for the…
  248. // current level (wave) the player is on.  It determines how many…
  249. // are required and then draws these offscreen.  It also flashes…
  250. // the obelisks and strikes the lightning.
  251.  
  252. void SetUpLevel (void)
  253. {
  254.     short        wasLedges, waveMultiple;
  255.     
  256.     KillOffEye();                    // Return eye to the aether.
  257.     
  258.     wasLedges = numLedges;            // Remember number of ledges.
  259.     waveMultiple = levelOn % 5;        // Waves repeat every 5th wave (but harder!).
  260.     
  261.     switch (waveMultiple)            // See which of the 5 we're on.
  262.     {
  263.         case 0:                        // Waves 0, 5, 10, …
  264.         numLedges = 5;                // have 5 ledges (platforms) on screen.
  265.         break;
  266.         
  267.         case 1:                        // Waves 1, 6, 11, …
  268.         numLedges = 6;                // are up to 6 ledges (platforms) on screen.
  269.         break;
  270.         
  271.         case 2:                        // Waves 2, 7, 12, …
  272.         numLedges = 5;                // return to 5 ledges (platforms) on screen.
  273.         break;
  274.         
  275.         case 3:                        // Waves 3, 8, 13, …
  276.         numLedges = 3;                // drop to 3 ledges (platforms) on screen.
  277.         break;
  278.         
  279.         case 4:                        // Waves 4, 9, 14, …
  280.         numLedges = 6;                // and return to 6 ledges (platforms) on screen.
  281.         break;
  282.     }
  283.     
  284.     if (wasLedges != numLedges)        // No need to redraw if platforms are unchanged.
  285.         DrawPlatforms(numLedges);
  286.     
  287.     FlashObelisks(TRUE);            // Flash the obelisks.
  288.     
  289.     GenerateLightning(320, 429);    // Lightning strikes platform 0.
  290.     StrikeLightning();
  291.     LogNextTick(2);
  292.     WaitForNextTick();
  293.     StrikeLightning();
  294.     
  295.     GenerateLightning(95, 289);        // Lightning strikes platform 1.
  296.     StrikeLightning();
  297.     LogNextTick(2);
  298.     WaitForNextTick();
  299.     StrikeLightning();
  300.     
  301.     GenerateLightning(95, 110);        // Lightning strikes platform 3.
  302.     StrikeLightning();
  303.     LogNextTick(2);
  304.     WaitForNextTick();
  305.     StrikeLightning();
  306.     
  307.     GenerateLightning(320, 195);    // Lightning strikes platform 5.
  308.     StrikeLightning();
  309.     LogNextTick(2);
  310.     WaitForNextTick();
  311.     StrikeLightning();
  312.     
  313.     GenerateLightning(545, 110);    // Lightning strikes platform 4.
  314.     StrikeLightning();
  315.     LogNextTick(2);
  316.     WaitForNextTick();
  317.     StrikeLightning();
  318.     
  319.     GenerateLightning(545, 289);    // Lightning strikes platform 2.
  320.     StrikeLightning();
  321.     LogNextTick(2);
  322.     WaitForNextTick();
  323.     StrikeLightning();
  324.     
  325.     FlashObelisks(FALSE);            // "Unflash" obelisks (return to normal state).
  326.                                     // Play lightning sound!
  327.     PlayExternalSound(kLightningSound, kLightningPriority);
  328.     
  329.     UpdateLevelNumbers();            // Display the current level on screen.
  330. }
  331.  
  332. //--------------------------------------------------------------  ResetPlayer
  333.  
  334. // This function prepares the player - it places the player and his/her mount…
  335. // in their proper starting location (depending on which platform they are to…
  336. // begin on), and it sets all the player's variables to their initial state.
  337.  
  338. void ResetPlayer (Boolean initialPlace)
  339. {
  340.     short        location;
  341.     
  342.     thePlayer.srcNum = 5;            // Set which graphic (frame) the player is to use.
  343.     thePlayer.frame = 320;            // This variable will be used as a coutndown timer.
  344.     
  345.     if (initialPlace)                // If "initialPlace" is TRUE, …
  346.         location = 0;                // the player is to begin on the lowest platform.
  347.     else                            // Otherwise, a random location is chosen.
  348.         location = RandomInt(numLedges);
  349.     
  350.     switch (location)                // Move player horizontally and vertically to their…
  351.     {                                // proper location (based on ledge # they're on).
  352.         case 0:
  353.         thePlayer.h = 296 << 4;        // Bottom center ledge.
  354.         thePlayer.v = 377 << 4;        // We're scaling by 16.
  355.         break;
  356.         
  357.         case 1:
  358.         thePlayer.h = 102 << 4;        // Lower left ledge.
  359.         thePlayer.v = 237 << 4;
  360.         break;
  361.         
  362.         case 2:
  363.         thePlayer.h = 489 << 4;        // Lower right ledge.
  364.         thePlayer.v = 237 << 4;
  365.         break;
  366.         
  367.         case 3:
  368.         thePlayer.h = 102 << 4;        // Top left ledge.
  369.         thePlayer.v = 58 << 4;
  370.         break;
  371.         
  372.         case 4:
  373.         thePlayer.h = 489 << 4;        // Top right ledge.
  374.         thePlayer.v = 58 << 4;
  375.         break;
  376.         
  377.         case 5:
  378.         thePlayer.h = 296 << 4;        // Top central ledge.
  379.         thePlayer.v = 143 << 4;
  380.         break;
  381.     }
  382.                                     // Assign destination rectangle.
  383.     thePlayer.dest = playerRects[thePlayer.srcNum];
  384.     ZeroRectCorner(&thePlayer.dest);
  385.     OffsetRect(&thePlayer.dest, thePlayer.h >> 4, thePlayer.v >> 4);
  386.     thePlayer.wasDest = thePlayer.dest;
  387.     
  388.     thePlayer.hVel = 0;                // Player initially has no velocity.
  389.     thePlayer.vVel = 0;
  390.     thePlayer.facingRight = TRUE;    // We're facing to the right.
  391.     thePlayer.flapping = FALSE;        // We're not flapping our wings initially.
  392.     thePlayer.wrapping = FALSE;        // We can't be wrapping around the edge of the screen.
  393.     thePlayer.clutched = FALSE;        // The hand ain't got us.
  394.     thePlayer.mode = kIdle;            // Our mode is "idle" - waiting to be "born".
  395.     if (lightningCount == 0)        // Prepare for a lightning display to "birth" us.
  396.     {
  397.         lightH = thePlayer.dest.left + 24;
  398.         lightV = thePlayer.dest.bottom - 24;
  399.         lightningCount = kNumLightningStrikes;
  400.     }
  401. }
  402.  
  403. //--------------------------------------------------------------  OffAMortal
  404.  
  405. // Alas, 'tis here that a player is brought who loses a life.
  406.  
  407. void OffAMortal (void)
  408. {
  409.     livesLeft--;                // Decrememnt number of player lives left.
  410.     
  411.     if (livesLeft > 0)            // Indeed, are there lives remaining?
  412.     {
  413.         ResetPlayer(FALSE);        // Good, start a new one off.
  414.         UpdateLivesNumbers();    // Make note of the number of lives remaining.
  415.     }
  416.     else                        // Otherwise, we are at the dreaded "Game Over".
  417.         playing = FALSE;        // Set flag to drop us out of game loop.
  418. }
  419.  
  420. //--------------------------------------------------------------  DoCommandKey
  421.  
  422. // This function handles the case when the user has held down the command…
  423. // key.  Note, this only applies to input when a game is in session - otherwise…
  424. // a standard event loop handles command keys and everything else.
  425.  
  426. void DoCommandKey (void)
  427. {
  428.     if (BitTst(&theKeys, kEKeyMap))            // Test for "command - E"…
  429.     {
  430.         playing = FALSE;                    // which would indicate "End Game".
  431.     }
  432.     else if (BitTst(&theKeys, kPKeyMap))    // Otherwise, see if it's "command - P".
  433.     {
  434.         pausing = TRUE;                        // This means the player is pausing the game.
  435.         MenusReflectMode();                    // Gray-out menus etc.
  436.         DumpMainToWorkMap();                // Save screen to offscreen.
  437.     }
  438.     else if (BitTst(&theKeys, kQKeyMap))    // Or perhaps the player hit "command - Q".
  439.     {
  440.         playing = FALSE;                    // Set flag to drop out of game loop.
  441.         quitting = TRUE;                    // Set flag to drop out of Glypha.
  442.     }
  443. }
  444.  
  445. //--------------------------------------------------------------  GetPlayerInput
  446.  
  447. // This function looks for keystrokes when a game is underway.  We don't use…
  448. // the more conventional event routines (like GetNextEvent()), because they're…
  449. // notoriously slow, allow background tasks, introduce possible INIT problems,…
  450. // and we don't have to.  Instead, we'll rely on GetKeys() (which has its own…
  451. // set of problems - but we deal with them).
  452.  
  453. void GetPlayerInput (void)
  454. {
  455.     thePlayer.flapping = FALSE;                // Assume we're not flapping.
  456.     thePlayer.walking = FALSE;                // Assume too we're not walking.
  457.  
  458.     GetKeys(theKeys);                        // Get the current keyboard keymap.
  459.     
  460. #if GENERATINGPOWERPC
  461.     // InputSprocket!
  462.     {
  463.         Boolean left = false;
  464.         Boolean right = false;
  465.         Boolean button = false;
  466.         
  467.         EasyJoystick(&left, &right, &button);
  468.         
  469.         if (button)    { BitSet(&theKeys, kDownArrowKeyMap); }
  470.         if (left) { BitSet(&theKeys, kLeftArrowKeyMap); }
  471.         if (right) { BitSet(&theKeys, kRightArrowKeyMap); }
  472.     }
  473. #endif
  474.     
  475.     if (BitTst(&theKeys, kCommandKeyMap))    // See first if command key down…
  476.         DoCommandKey();                        // and handle those seperately.
  477.     else                                    // If not command key, continue.
  478.     {                                        // Look for one of the two "flap" keys.
  479.         if ((BitTst(&theKeys, kSpaceBarMap)) || (BitTst(&theKeys, kDownArrowKeyMap)))
  480.         {
  481.             if (thePlayer.mode == kIdle)    // Handle special case when player is idle.
  482.             {
  483.                 thePlayer.mode = kWalking;    // Set the player's mode now to walking.
  484.                 thePlayer.frame = 0;        // Used to note "state" of walking.
  485.             }                                // Otherwise, if player is flying or walking…
  486.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  487.             {
  488.                 if (!flapKeyDown)            // If flap key was not down last frame…
  489.                 {                            // (this is to prevent "automatic fire").
  490.                                             // Give player lift.
  491.                     thePlayer.vVel -= kFlapImpulse;
  492.                     flapKeyDown = TRUE;        // Note that the flap key is down.
  493.                                             // Play the "flap" sound.
  494.                     PlayExternalSound(kFlapSound, kFlapPriority);
  495.                                             // Set player flag to indicate flapping.
  496.                     thePlayer.flapping = TRUE;
  497.                 }
  498.             }
  499.         }
  500.         else
  501.             flapKeyDown = FALSE;            // If flap key not down, remember this.
  502.         
  503.                                             // Test now for one of three "right" keys.
  504.         if ((BitTst(&theKeys, kRightArrowKeyMap) || 
  505.                 BitTst(&theKeys, kSKeyMap) || 
  506.                 BitTst(&theKeys, kQuoteMap)) && 
  507.                 (thePlayer.hVel < kMaxHVelocity))
  508.         {
  509.             if (thePlayer.mode == kIdle)    // Handle special case when player idle.
  510.             {                                // They are to begin walking (no longer idle).
  511.                 thePlayer.mode = kWalking;
  512.                 thePlayer.frame = 0;
  513.             }
  514.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  515.             {                                // If flying or walking, player moves right.
  516.                 if (!thePlayer.facingRight)    // If facing left, player does an about face.
  517.                 {
  518.                     thePlayer.facingRight = TRUE;
  519.                     if (thePlayer.clutched)
  520.                     {
  521.                         thePlayer.dest.left += 18;
  522.                         thePlayer.dest.right += 18;                        
  523.                         thePlayer.h = thePlayer.dest.left << 4;
  524.                         thePlayer.wasH = thePlayer.h;
  525.                         thePlayer.wasDest = thePlayer.dest;
  526.                     }
  527.                 }                            // Otherwise, if facing right already…
  528.                 else
  529.                 {                            // If flying, add to their horizontal velocity.
  530.                     if (thePlayer.mode == kFlying)
  531.                         thePlayer.hVel += kGlideImpulse;
  532.                     else                    // If walking, set flag to indicate a step.
  533.                         thePlayer.walking = TRUE;
  534.                 }
  535.             }
  536.         }                                    // Test now for one of three "left" keys.
  537.         else if ((BitTst(&theKeys, kLeftArrowKeyMap) || 
  538.                 BitTst(&theKeys, kAKeyMap) || 
  539.                 BitTst(&theKeys, kColonMap)) && 
  540.                 (thePlayer.hVel > -kMaxHVelocity))
  541.         {
  542.             if (thePlayer.mode == kIdle)    // Handle special case when player idle.
  543.             {
  544.                 thePlayer.mode = kWalking;
  545.                 thePlayer.frame = 0;
  546.             }
  547.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  548.             {                                // If flying or walking, player moves left.
  549.                 if (thePlayer.facingRight)    // If facing right, player does an about face.
  550.                 {                            // Flag player facing left.
  551.                     thePlayer.facingRight = FALSE;
  552.                     if (thePlayer.clutched)    // Handle case where player gripped by hand.
  553.                     {                        // An about face handled a bit differently.
  554.                         thePlayer.dest.left -= 18;
  555.                         thePlayer.dest.right -= 18;
  556.                         thePlayer.h = thePlayer.dest.left << 4;
  557.                         thePlayer.wasH = thePlayer.h;
  558.                         thePlayer.wasDest = thePlayer.dest;
  559.                     }
  560.                 }
  561.                 else                        // Otherwise, player already facing left.
  562.                 {                            // So player will move left.
  563.                     if (thePlayer.mode == kFlying)
  564.                         thePlayer.hVel -= kGlideImpulse;
  565.                     else
  566.                         thePlayer.walking = TRUE;
  567.                 }
  568.             }
  569.         }
  570.     }
  571. }
  572.  
  573. //--------------------------------------------------------------  HandlePlayerIdle
  574.  
  575. // Following are a number of functions handling the player's different "modes".
  576. // This first function handles the player when in "idle" mode.  When idle, the…
  577. // player is standing on a platform - having just been "born".  This is when the…
  578. // player is in a "safe" mode - meaning no enemy can kill them.  The player remains…
  579. // in idle mode until they hit a key to flap or move or until a timer (thePlayer.frame)…
  580. // counts down to zero.
  581.  
  582. void HandlePlayerIdle (void)
  583. {
  584.     thePlayer.frame--;                // Count down the timer.
  585.     if (thePlayer.frame == 0)        // See if timer has reached zero yet.
  586.         thePlayer.mode = kWalking;    // If so, player is no longer idle.
  587.     
  588.     SetAndCheckPlayerDest();        // Keep player on platform.
  589. }
  590.  
  591. //--------------------------------------------------------------  HandlePlayerFlying
  592.  
  593. // This function handles a player in "flying" mode.  In flying mode, the player…
  594. // is alive and not standing/walking on any platform.  A plyaer remains in flying…
  595. // mode until the player dies (collides unfavorably with an enemy), is caught by…
  596. // the hand, or comes near the top of a platform (in which case they land and…
  597. // switch to walking mode).  While in flying mode, gravity pulls the player down…
  598. // while friction acts to slow the player down.
  599.  
  600. void HandlePlayerFlying (void)
  601. {    
  602.     if (thePlayer.hVel > 0)                    // If player has a positive hori. velocity…
  603.     {                                        // subtract frictional constant from velocity.
  604.         thePlayer.hVel -= kAirResistance;
  605.         if (thePlayer.hVel < 0)                // Don't let it go negative (otherwise, you…
  606.             thePlayer.hVel = 0;                // can get a "yo-yo" effect set up).
  607.     }
  608.     else if (thePlayer.hVel < 0)            // Otherwise, if horizontal velocity negative…
  609.     {                                        // add firctional constant to hori. velocity.
  610.         thePlayer.hVel += kAirResistance;
  611.         if (thePlayer.hVel > 0)
  612.             thePlayer.hVel = 0;
  613.     }
  614.     
  615.     thePlayer.vVel += kGravity;                // Add gravity to player's vertical velocity.
  616.     
  617.     if (thePlayer.vVel > kMaxVVelocity)        // Don't allow player to fall too fast.
  618.         thePlayer.vVel = kMaxVVelocity;
  619.     else if (thePlayer.vVel < -kMaxVVelocity)
  620.         thePlayer.vVel = -kMaxVVelocity;    // And don't allow player to climb too fast.
  621.     
  622.     thePlayer.h += thePlayer.hVel;            // Add velocities to players position.
  623.     thePlayer.v += thePlayer.vVel;
  624.                                             // Now we determine which graphic to use.
  625.     if (thePlayer.facingRight)                // There are the set of right-facing graphics.
  626.     {
  627.         thePlayer.srcNum = 1;                // Assume standard right-facing graphic.
  628.         if (thePlayer.vVel < -kDontFlapVel)    // Now we jump through a series of hoops…
  629.         {                                    // simply to determine whether we'll use…
  630.             if (thePlayer.flapping)            // the graphic of the player with the wings…
  631.                 thePlayer.srcNum = 0;        // up (srcNum = 0) or with the wings down…
  632.             else                            // (srcNum = 1).
  633.                 thePlayer.srcNum = 1;
  634.         }
  635.         else if (thePlayer.vVel > kDontFlapVel)
  636.         {
  637.             if (thePlayer.flapping)
  638.                 thePlayer.srcNum = 1;
  639.             else
  640.                 thePlayer.srcNum = 0;
  641.         }
  642.         else if (thePlayer.flapping)
  643.             thePlayer.srcNum = 0;
  644.     }
  645.     else                                    // If the player is facing left…
  646.     {                                        // We jump through a similar set of hoops…
  647.         thePlayer.srcNum = 2;                // this time choosing between srcNum = 2 …
  648.         if (thePlayer.vVel < -kDontFlapVel)    // and srcNum = 3.
  649.         {
  650.             if (thePlayer.flapping)
  651.                 thePlayer.srcNum = 3;
  652.             else
  653.                 thePlayer.srcNum = 2;
  654.         }
  655.         else if (thePlayer.vVel > kDontFlapVel)
  656.         {
  657.             if (thePlayer.flapping)
  658.                 thePlayer.srcNum = 2;
  659.             else
  660.                 thePlayer.srcNum = 3;
  661.         }
  662.         else if (thePlayer.flapping)
  663.             thePlayer.srcNum = 3;
  664.     }
  665.     
  666.     SetAndCheckPlayerDest();                // Check for wrap-around, etc.
  667.     
  668.     CheckLavaRoofCollision();                // See if player hit top or bottom of screen.
  669.     CheckPlayerEnemyCollision();            // See if player hit an enemy.
  670.     CheckPlatformCollision();                // See if player collided with platform.
  671.     CheckTouchDownCollision();                // See if player has landed on platform.
  672. }
  673.  
  674. //--------------------------------------------------------------  HandlePlayerWalking
  675.  
  676. // This function handles a player in "walking" mode.  They remain in this mode…
  677. // until they walk off a platform's edge, flap to lift off the platform, or…
  678. // collide unfavorably with an enemy (die).  While in walking mode, we need only…
  679. // determine which frame of animation to display (if the player is taking steps)…
  680. // and check for the usual set of collisions.
  681.  
  682. void HandlePlayerWalking (void)
  683. {
  684.     short        desiredHVel;
  685.     
  686.     if (thePlayer.walking)                    // This means user is actively holding down…
  687.     {                                        // the left or right key.
  688.         if (evenFrame)                        // Now we jump through a number of hoops…
  689.         {                                    // in order to get a semi-realistic…
  690.             if (thePlayer.facingRight)        // "stepping" animation going.  We take steps…
  691.             {                                // only on "even frames".
  692.                 if (thePlayer.srcNum == 4)
  693.                     desiredHVel = 208;
  694.                 else
  695.                     desiredHVel = 128;
  696.             }
  697.             else
  698.             {
  699.                 if (thePlayer.srcNum == 7)
  700.                     desiredHVel = -208;
  701.                 else
  702.                     desiredHVel = -128;
  703.             }
  704.             
  705.             if (thePlayer.hVel < desiredHVel)
  706.             {
  707.                 thePlayer.hVel += 80;        // Move player right.
  708.                 if (thePlayer.hVel > desiredHVel)
  709.                 {                            // This is the case where player is walking.
  710.                     thePlayer.hVel = desiredHVel;
  711.                     PlayExternalSound(kWalkSound, kWalkPriority);
  712.                 }
  713.                 else                        // In this case, player is skidding.
  714.                     PlayExternalSound(kScreechSound, kScreechPriority);
  715.             }
  716.             else
  717.             {
  718.                 thePlayer.hVel -= 80;        // Move player to the left.
  719.                 if (thePlayer.hVel < desiredHVel)
  720.                 {                            // Player is stepping to left.
  721.                     thePlayer.hVel = desiredHVel;
  722.                     PlayExternalSound(kWalkSound, kWalkPriority);
  723.                 }
  724.                 else                        // Player is skidding to a stop.
  725.                     PlayExternalSound(kScreechSound, kScreechPriority);
  726.             }
  727.         }
  728.     }
  729.     else                                    // If user is not actively holding down the…
  730.     {                                        // left or right key, bring player to a stop.
  731.         thePlayer.hVel -= thePlayer.hVel / 4;
  732.         if ((thePlayer.hVel < 4) && (thePlayer.hVel > -4))
  733.             thePlayer.hVel = 0;                // If close to zero (within 4), stop player.
  734.         else                                // Othewrwise, play the skidding sound.
  735.             PlayExternalSound(kScreechSound, kScreechPriority);
  736.     }
  737.     
  738.     if (thePlayer.vVel > kMaxVVelocity)        // Keep player from moving too quickly…
  739.         thePlayer.vVel = kMaxVVelocity;        // left or right.
  740.     else if (thePlayer.vVel < -kMaxVVelocity)
  741.         thePlayer.vVel = -kMaxVVelocity;
  742.     
  743.     thePlayer.h += thePlayer.hVel;            // Move player horizontally and vertically…
  744.     thePlayer.v += thePlayer.vVel;            // by the corresponding velocity.
  745.     
  746.     if (thePlayer.walking)                    // "If player holding down left or right keys…".
  747.     {
  748.         if (evenFrame)                        // Here's where we toggle between the two…
  749.         {                                    // frames of "stepping" animation.
  750.             if (thePlayer.facingRight)
  751.                 thePlayer.srcNum = 9 - thePlayer.srcNum;
  752.             else
  753.                 thePlayer.srcNum = 13 - thePlayer.srcNum;
  754.         }
  755.     }
  756.     else                                    // If the player not holding down keys…
  757.     {                                        // draw the player just standing there.
  758.         if (thePlayer.facingRight)
  759.             thePlayer.srcNum = 5;
  760.         else
  761.             thePlayer.srcNum = 6;
  762.     }
  763.     
  764.     SetAndCheckPlayerDest();                // Check for wrap-around and all that.
  765.     
  766.     CheckTouchDownCollision();                // See if player still on platform.
  767.     KeepPlayerOnPlatform();                    // Don't let player "sink through" ledge.
  768.     CheckPlayerEnemyCollision();            // See if player hit an enemy.
  769. }
  770.  
  771. //--------------------------------------------------------------  HandlePlayerSinking
  772.  
  773. // When the player is in "sinking" mode, they are on a one-way ticket to death.
  774. // The player is sinking into the lava.  We put the player into this mode (rather…
  775. // than kill them outright) so that we can have a number of frames of them slowly…
  776. // slipping beneath the surface of the lava.  When the get below the surface of…
  777. // the lava, they will be officially "killed" and a new player will be "born",
  778.  
  779. void HandlePlayerSinking (void)
  780. {
  781.     thePlayer.hVel = 0;                        // Don't allow horizontal motion.
  782.     thePlayer.vVel = 16;                    // They will sink at this constant rate.
  783.     if (thePlayer.dest.top > kLavaHeight)    // See if they slipped below the surface.
  784.         OffAMortal();                        // If they did, kill 'em.
  785.     
  786.     thePlayer.v += thePlayer.vVel;            // Otherwise, move them down a notch.
  787.     
  788.     SetAndCheckPlayerDest();                // Check for wrap-around, etc.
  789. }
  790.  
  791. //--------------------------------------------------------------  HandlePlayerFalling
  792.  
  793. // "Falling" refers to a player who is dead already but is still careening…
  794. // down the screen as a skeleton.  If (when) the player lands on a ledge they…
  795. // will turn into a pile of bones for a short duration.  If instead they fall…
  796. // into the lava, they'll sink.  In any event, it is then that they are…
  797. // officially pronounced dead and a new player is born.
  798.  
  799. void HandlePlayerFalling (void)
  800. {
  801.     if (thePlayer.hVel > 0)                // Handle horizontal air resistance.
  802.     {
  803.         thePlayer.hVel -= kAirResistance;
  804.         if (thePlayer.hVel < 0)
  805.             thePlayer.hVel = 0;
  806.     }
  807.     else if (thePlayer.hVel < 0)
  808.     {
  809.         thePlayer.hVel += kAirResistance;
  810.         if (thePlayer.hVel > 0)
  811.             thePlayer.hVel = 0;
  812.     }
  813.     
  814.     thePlayer.vVel += kGravity;            // Add in effect of gravity.
  815.     
  816.     if (thePlayer.vVel > kMaxVVelocity)    // Keep player from falling too fast.
  817.         thePlayer.vVel = kMaxVVelocity;
  818.     else if (thePlayer.vVel < -kMaxVVelocity)
  819.         thePlayer.vVel = -kMaxVVelocity;
  820.     
  821.     thePlayer.h += thePlayer.hVel;        // Move player's x and y (h and v)…
  822.     thePlayer.v += thePlayer.vVel;        // by amount of velocity in each direction.
  823.     
  824.     SetAndCheckPlayerDest();            // Check for wrap-around, etc.
  825.     
  826.     CheckLavaRoofCollision();            // See if they hit roof or lava.
  827.     CheckPlatformCollision();            // See if they crashed to a ledge.
  828. }
  829.  
  830. //--------------------------------------------------------------  HandlePlayerBones
  831.  
  832. // This is when the player is just a static pile of bones on a platform.  They…
  833. // have been killed by an enemy and now are waiting to slip away so that a new…
  834. // player can be born.
  835.  
  836. void HandlePlayerBones (void)
  837. {
  838.     if (evenFrame)                    // To slow it down a bit, action only occurs…
  839.     {                                // on the even frames.
  840.         thePlayer.frame--;            // Drop the counter down by one.
  841.         if (thePlayer.frame == 0)    // When counter reaches zero, player officially dies.
  842.             OffAMortal();
  843.         else                        // Otherwise, player's bones are sinking.
  844.             thePlayer.dest.top = thePlayer.dest.bottom - thePlayer.frame;
  845.     }
  846. }
  847.  
  848. //--------------------------------------------------------------  MovePlayer
  849.  
  850. // This function is the sort of "master movement" function.  It looks…
  851. // at what mode a player is in and calls the appropriate function from…
  852. // above.  Arcade games (at least this one) tend to be very "modal" in…
  853. // this way.  It's the actions of the user and the enemies in the game…
  854. // that cause the player's mode to move from one state to another.
  855.  
  856. void MovePlayer (void)
  857. {
  858.     switch (thePlayer.mode)        // Check the "mode" the player is in.
  859.     {
  860.         case kIdle:                // Invulnerable - standing there - just born.
  861.         HandlePlayerIdle();
  862.         break;
  863.         
  864.         case kFlying:            // Flapping, floating, airborne.
  865.         HandlePlayerFlying();
  866.         break;
  867.         
  868.         case kWalking:            // On terra firma.  Standing or walking on ledge.
  869.         HandlePlayerWalking();
  870.         break;
  871.         
  872.         case kSinking:            // Trapped in the lava - going down.
  873.         HandlePlayerSinking();
  874.         break;
  875.         
  876.         case kFalling:            // Dead - a skeleton falling to earth.
  877.         HandlePlayerFalling();
  878.         break;
  879.         
  880.         case kBones:            // Dead - a static pile of bones on a ledge.
  881.         HandlePlayerBones();
  882.         break;
  883.     }
  884. }
  885.  
  886. //--------------------------------------------------------------  CheckTouchDownCollision
  887.  
  888. // This function determines whether or not the player is landed on a ledge.
  889. // It does this by doing a rectangle collision between the player's bounding…
  890. // rectangle and an imaginary rectangle enclosing an area above the ledges.
  891. // I call these imaginary rectangles "touchDownRects[]".  The trick was that…
  892. // you don't want the player to have to "hit" the top of a ledge in order to…
  893. // land on it - there is an arbitrary distance above a ledge where, if the player…
  894. // is within this area, the legs ought to come out and the player flagged as…
  895. // walking.  As well, this same function is used for a walking player to see…
  896. // if they are still on the ledge (they may walk off the edge).
  897.  
  898. void CheckTouchDownCollision (void)
  899. {
  900.     Rect        testRect, whoCares;
  901.     short        i, offset;
  902.     Boolean        sected;
  903.     
  904.     sected = FALSE;                                // Assume not on ledge.
  905.     for (i = 0; i < numLedges; i++)                // Go through all ledges.
  906.     {
  907.         testRect = touchDownRects[i];            // Here's the imaginary rect.
  908.         if (thePlayer.mode == kWalking)            // We need an offset if player walking…
  909.             OffsetRect(&testRect, 0, 11);        // since the player graphic is taller.
  910.         
  911.         if (SectRect(&thePlayer.dest, &testRect, &whoCares))
  912.         {                                        // Does the player's rect intersect?
  913.             if (thePlayer.mode == kFlying)        // Okay, it does, is the player airborne?
  914.             {
  915.                 thePlayer.mode = kWalking;        // Put player into walking mode.
  916.                 if (thePlayer.facingRight)        // Assign correct graphic for player.
  917.                     thePlayer.srcNum = 5;
  918.                 else
  919.                     thePlayer.srcNum = 6;
  920.                 if (thePlayer.vVel > 0)            // Stop player from falling further.
  921.                     thePlayer.vVel = 0;
  922.                 thePlayer.dest.bottom += 11;    // "Grow" player's bounding rect.
  923.                 thePlayer.wasDest.bottom += 11;
  924.                                                 // Move player so standing on top of ledge.
  925.                 offset = thePlayer.dest.bottom - testRect.bottom - 1;
  926.                 thePlayer.dest.bottom -= offset;
  927.                 thePlayer.dest.top -= offset;
  928.                 thePlayer.v = thePlayer.dest.top << 4;
  929.                                                 // Play brief collision sound.
  930.                 PlayExternalSound(kGrateSound, kGratePriority);
  931.             }
  932.             sected = TRUE;                        // Make note that we've landed.
  933.         }
  934.     }
  935.     
  936.     if (!sected)                                // Now, if we didn't collide…
  937.     {                                            // were we walking?
  938.         if (thePlayer.mode == kWalking)            // Did we walk off the ledge?
  939.         {
  940.             thePlayer.mode = kFlying;            // Set player to flying mode.
  941.             thePlayer.dest.bottom -= 11;        // Resize player's bounding rect.
  942.             thePlayer.wasDest.bottom -= 11;
  943.         }
  944.     }
  945. }
  946.  
  947. //--------------------------------------------------------------  CheckPlatformCollision
  948.  
  949. // Unlike the above function, this one tests the player's bounding rect against…
  950. // the bounding rect of each ledge (not an imaginary rect above the ledge).  This…
  951. // function is primarily for (then) collisions off the bottom and sides of the…
  952. // ledges.  In this way, the ledges are "solid" - not able to be passed through.
  953.  
  954. void CheckPlatformCollision (void)
  955. {
  956.     Rect        hRect, vRect, whoCares;
  957.     short        i, offset;
  958.     
  959.     for (i = 0; i < numLedges; i++)                    // Walk through all ledges.
  960.     {                                                // Test rectangle overlap.
  961.         if (SectRect(&thePlayer.dest, &platformRects[i], &whoCares))
  962.         {                                            // If player intersecting ledge…
  963.             hRect.left = thePlayer.dest.left;        // Create our special test rect.
  964.             hRect.right = thePlayer.dest.right;
  965.             hRect.top = thePlayer.wasDest.top;
  966.             hRect.bottom = thePlayer.wasDest.bottom;
  967.                                                     // Determine if the player hit the…
  968.                                                     // top/bottom of the ledge or the…
  969.                                                     // sides of the ledge.
  970.             if (SectRect(&hRect, &platformRects[i], &whoCares))
  971.             {                                        // We're fairly sure the player hit…
  972.                                                     // the left or right edge of ledge.
  973.                 if (thePlayer.h > thePlayer.wasH)    // If player was heading right…
  974.                 {                                    // player will bounce to left.
  975.                     offset = thePlayer.dest.right - platformRects[i].left;
  976.                     thePlayer.dest.left -= offset;
  977.                     thePlayer.dest.right -= offset;
  978.                     thePlayer.h = thePlayer.dest.left << 4;
  979.                     if (thePlayer.hVel > 0)            // We bounce back with 1/2 our vel.
  980.                         thePlayer.hVel = -(thePlayer.hVel >> 1);
  981.                     else
  982.                         thePlayer.hVel = thePlayer.hVel >> 1;
  983.                 }                                    // Else if player was heading left…
  984.                 else if (thePlayer.h < thePlayer.wasH)
  985.                 {                                    // player will bounce right.
  986.                     offset = platformRects[i].right - thePlayer.dest.left;
  987.                     thePlayer.dest.left += offset;
  988.                     thePlayer.dest.right += offset;
  989.                     thePlayer.h = thePlayer.dest.left << 4;
  990.                     if (thePlayer.hVel < 0)            // We bounce back with 1/2 our vel.
  991.                         thePlayer.hVel = -(thePlayer.hVel >> 1);
  992.                     else
  993.                         thePlayer.hVel = thePlayer.hVel >> 1;
  994.                 }                                    // Play impact sound.
  995.                 PlayExternalSound(kGrateSound, kGratePriority);
  996.             }
  997.             else                                    // It doesn't look like we hit the…
  998.             {                                        // the left or right edge of ledge.
  999.                 vRect.left = thePlayer.wasDest.left;
  1000.                 vRect.right = thePlayer.wasDest.right;
  1001.                 vRect.top = thePlayer.dest.top;
  1002.                 vRect.bottom = thePlayer.dest.bottom;
  1003.                                                     // So we'll test top/bottom collision.
  1004.                 if (SectRect(&vRect, &platformRects[i], &whoCares))
  1005.                 {                                    // We've decided we've hit top/bottom.
  1006.                     if (thePlayer.wasV < thePlayer.v)
  1007.                     {                                // If we were heading down (hit top)…
  1008.                                                     // keep player on top of ledge.
  1009.                         offset = thePlayer.dest.bottom - platformRects[i].top;
  1010.                         thePlayer.dest.top -= offset;
  1011.                         thePlayer.dest.bottom -= offset;
  1012.                         thePlayer.v = thePlayer.dest.top << 4;
  1013.                                                     // Play collision sound.
  1014.                         if (thePlayer.vVel > kDontFlapVel)
  1015.                             PlayExternalSound(kGrateSound, kGratePriority);
  1016.                                                     // If we were falling bones (dead)…
  1017.                         if (thePlayer.mode == kFalling)
  1018.                         {                            // we'll bounce.
  1019.                             if ((thePlayer.dest.right - 16) > platformRects[i].right)                            {
  1020.                                 thePlayer.hVel = 16;
  1021.                                 if (thePlayer.vVel > 0)
  1022.                                     thePlayer.vVel = -(thePlayer.vVel >> 1);
  1023.                                 else
  1024.                                     thePlayer.vVel = thePlayer.vVel >> 1;
  1025.                             }
  1026.                             else if ((thePlayer.dest.left + 16) < platformRects[i].left)
  1027.                             {
  1028.                                 thePlayer.hVel = -16;
  1029.                                 if (thePlayer.vVel > 0)
  1030.                                     thePlayer.vVel = -(thePlayer.vVel >> 1);
  1031.                                 else
  1032.                                     thePlayer.vVel = thePlayer.vVel >> 1;
  1033.                             }
  1034.                             else                    // If we were nearly stopped…
  1035.                             {                        // turn into pile of bones.
  1036.                                 PlayExternalSound(kBoom1Sound, kBoom1Priority);
  1037.                                 thePlayer.vVel = 0;
  1038.                                 thePlayer.mode = kBones;
  1039.                                 thePlayer.frame = 22;
  1040.                                 thePlayer.dest.top = thePlayer.dest.bottom - 22;
  1041.                                 thePlayer.v = thePlayer.dest.top << 4;
  1042.                                 thePlayer.srcNum = 10;
  1043.                             }
  1044.                         }
  1045.                         else                        // Okay, if we weren't falling bones…
  1046.                         {                            // bounce the player (-1/2 vel.).
  1047.                             if (thePlayer.vVel > 0)
  1048.                                 thePlayer.vVel = -(thePlayer.vVel >> 1);
  1049.                             else
  1050.                                 thePlayer.vVel = thePlayer.vVel >> 1;
  1051.                         }
  1052.                     }                                // If the player was instead moving up…
  1053.                     else if (thePlayer.wasV > thePlayer.v)
  1054.                     {                                // the player likely hit the bottom of…
  1055.                                                     // the ledge.  Keep player below ledge.
  1056.                         offset = platformRects[i].bottom - thePlayer.dest.top;
  1057.                         thePlayer.dest.top += offset;
  1058.                         thePlayer.dest.bottom += offset;
  1059.                         thePlayer.v = thePlayer.dest.top << 4;
  1060.                                                     // Play collision sound.
  1061.                         PlayExternalSound(kGrateSound, kGratePriority);
  1062.                         if (thePlayer.vVel < 0)        // Bounce player down (-1/2 vel.).
  1063.                             thePlayer.vVel = -(thePlayer.vVel >> 1);
  1064.                         else
  1065.                             thePlayer.vVel = thePlayer.vVel >> 1;
  1066.                     }
  1067.                 }
  1068.             }
  1069.         }
  1070.     }
  1071. }
  1072.  
  1073. //--------------------------------------------------------------  KeepPlayerOnPlatform
  1074.  
  1075. // This is an alignment function.  It is called only if the player is standing or…
  1076. // walking on a ledge.  It is designed to keep the player's mount's (bird's)…
  1077. // feet firmly planted on the ledge.  Consider that, with the addition of gravity…
  1078. // to a player's downward velocity, there is a problem where the player can appear…
  1079. // to slowly sink down through the ledge.  There may be any number of methods you…
  1080. // might want to try to prevent this from becoming a problem in the first place, …
  1081. // but my experience has been that all the methods I've tried have flaws - correcting…
  1082. // for those flaws points out other flaws and you start getting a messy sort of…
  1083. // patchwork.  Should you ever get it to work, the mess that is your function has come…
  1084. // to resemble the Knot of ????.
  1085.  
  1086. void KeepPlayerOnPlatform (void)
  1087. {
  1088.     Rect        whoCares;
  1089.     short        i, offset;
  1090.     
  1091.     for (i = 0; i < numLedges; i++)        // For each ledge for this wave…
  1092.     {                                    // test for a collision.
  1093.         if ((SectRect(&thePlayer.dest, &platformRects[i], &whoCares)) && (thePlayer.vVel > 0))
  1094.         {                                // If collided (player sinking), force…
  1095.                                         // player to top of ledge.
  1096.             offset = thePlayer.dest.bottom - platformRects[i].top - 1;
  1097.             thePlayer.dest.top -= offset;
  1098.             thePlayer.dest.bottom -= offset;
  1099.             thePlayer.v = thePlayer.dest.top * 16;
  1100.         }
  1101.     }
  1102.     
  1103.     if (thePlayer.vVel > 0)                // Set player's vertical velocity to zero.
  1104.         thePlayer.vVel = 0;
  1105. }
  1106.  
  1107. //--------------------------------------------------------------  CheckLavaRoofCollision
  1108.  
  1109. // This is a simple high/low test to see if the player has either bounced off…
  1110. // the roof of the "arena" or dipped down into the lava below.
  1111.  
  1112. void CheckLavaRoofCollision (void)
  1113. {
  1114.     short        offset;
  1115.     
  1116.     if (thePlayer.dest.bottom > kLavaHeight)    // See if player in lava.
  1117.     {
  1118.         if (thePlayer.mode == kFalling)            // If falling (dead), "Splash!"
  1119.             PlayExternalSound(kSplashSound, kSplashPriority);
  1120.         else                                    // If flying (alive), "Yeow!"
  1121.             PlayExternalSound(kBirdSound, kBirdPriority);
  1122.         thePlayer.mode = kSinking;                // Irregardless, player is now sinking.
  1123.     }
  1124.     else if (thePlayer.dest.top < kRoofHeight)    // See if player hit roof.
  1125.     {                                            // Move player to below roof.
  1126.         offset = kRoofHeight - thePlayer.dest.top;
  1127.         thePlayer.dest.top += offset;
  1128.         thePlayer.dest.bottom += offset;
  1129.         thePlayer.v = thePlayer.dest.top * 16;
  1130.                                                 // Play collision sound.
  1131.         PlayExternalSound(kGrateSound, kGratePriority);
  1132.         thePlayer.vVel = thePlayer.vVel / -2;    // Rebound player (-1/2 vel.).
  1133.     }
  1134. }
  1135.  
  1136. //--------------------------------------------------------------  SetAndCheckPlayerDest
  1137.  
  1138. // This function keeps our player's screen coordinates and "scaled" coordinates…
  1139. // in agreement.  As well, it checks for wrap-around and handles it.
  1140.  
  1141. void SetAndCheckPlayerDest (void)
  1142. {
  1143.     short        wasTall, wasWide;
  1144.                                         // Remember width and height of player.
  1145.     wasTall = thePlayer.dest.bottom - thePlayer.dest.top;
  1146.     wasWide = thePlayer.dest.right - thePlayer.dest.left;
  1147.                                         // Convert scaled coords to screen coords.
  1148.     thePlayer.dest.left = thePlayer.h >> 4;
  1149.     thePlayer.dest.right = thePlayer.dest.left + wasWide;
  1150.     thePlayer.dest.top = thePlayer.v >> 4;
  1151.     thePlayer.dest.bottom = thePlayer.dest.top + wasTall;
  1152.     
  1153.     if (thePlayer.dest.left > 640)        // Has player left right side of arena?
  1154.     {                                    // Wrap player back to left side of screen.
  1155.         OffsetRect(&thePlayer.dest, -640, 0);
  1156.         thePlayer.h = thePlayer.dest.left << 4;
  1157.         OffsetRect(&thePlayer.wasDest, -640, 0);
  1158.     }
  1159.     else if (thePlayer.dest.right < 0)    // Else, has player left left side of screen?
  1160.     {                                    // Wrap player around to right side of screen.
  1161.         OffsetRect(&thePlayer.dest, 640, 0);
  1162.         thePlayer.h = thePlayer.dest.left << 4;
  1163.         OffsetRect(&thePlayer.wasDest, 640, 0);
  1164.     }
  1165. }
  1166.  
  1167. //--------------------------------------------------------------  HandleLightning
  1168.  
  1169. // Lightning is handled here.  Obelisks are flashed, lightning is generated, …
  1170. // lighting strikes, and the lightning counter decremented.  This is pretty…
  1171. // nice - we can just set "lightningCount" to a non-zero number and this…
  1172. // function will strike lightning every fram until the counter returns to zero.
  1173.  
  1174. void HandleLightning (void)
  1175. {
  1176.     if (lightningCount > 0)                        // Is lightning to strik this frame?
  1177.     {                                            // Special frame when obelisks are lit.
  1178.         if (lightningCount == kNumLightningStrikes)
  1179.             FlashObelisks(TRUE);
  1180.         GenerateLightning(lightH, lightV);        // Create new lightning "segments".
  1181.         StrikeLightning();                        // Draw lightning on screen.
  1182.     }
  1183. }
  1184.  
  1185. //--------------------------------------------------------------  FinishLightning
  1186.  
  1187. // This undoes what the lightning did.  It "undraws" the lightning and returns…
  1188. // the obelisks to their "non lit" state.  I see that it is HERE where the counter…
  1189. // is decremented and not in the function above.
  1190.  
  1191. void FinishLightning (void)
  1192. {
  1193.     if (lightningCount > 0)
  1194.     {
  1195.         StrikeLightning();                        // Undraw lightning (exclusive Or).
  1196.         lightningCount--;                        // Descrement lightning counter.
  1197.         if (lightningCount == 0)                // If this is the last lightning strike…
  1198.             FlashObelisks(FALSE);                // return obelisk to normal.
  1199.                                                 // "BOOOOM!"
  1200.         PlayExternalSound(kLightningSound, kLightningPriority);
  1201.     }
  1202. }
  1203.  
  1204. //--------------------------------------------------------------  HandleCountDownTimer
  1205.  
  1206. // This is a pretty boring function.  It is here so that when one level ends,…
  1207. // the next one does begin immediately.  It gives the player a few seconds of…
  1208. // breathing time.  Essentially, to engage it, we need merely set "countDownTimer"…
  1209. // to a positive number.  Each frame the counter gets decremented.  When it…
  1210. // reaches zero, the level is advanced to the next wave.
  1211.  
  1212. void HandleCountDownTimer (void)
  1213. {
  1214.     if (countDownTimer == 0)        // If already zero, do nothing.
  1215.         return;
  1216.     else                            // Otherwise, if greater than zero…
  1217.     {
  1218.         countDownTimer--;            // decrememnt counter.
  1219.         if (countDownTimer == 0)    // Did it just hit zero?
  1220.         {
  1221.             countDownTimer = 0;        // Well, just to be sure (dumb line of code).
  1222.             levelOn++;                // Increment the level (wave) we're on.
  1223.             UpdateLevelNumbers();    // Display new level on screen.
  1224.             SetUpLevel();            // Set up the platforms.
  1225.             GenerateEnemies();        // Ready nemesis.
  1226.         }
  1227.     }
  1228. }
  1229.  
  1230. //--------------------------------------------------------------  PlayGame
  1231.  
  1232. // Here is the "core" of the "game loop".  When a player has elected to…
  1233. // begin a game, Glypha falls into this function and remains in a loop…
  1234. // herein until the player either quits, or loses their last "bird".
  1235. // Each pass through the main loop below constitutes one "frame" of the game.
  1236.  
  1237. void PlayGame (void)
  1238. {
  1239.     #define        kTicksPerFrame        2L
  1240.     Point        offsetPt;
  1241.     long        waitUntil;
  1242.     
  1243.     offsetPt.h = 0;                                // Set up ShieldCursor() point.
  1244.     offsetPt.v = 20;
  1245.     ShieldCursor(&mainWindowRect, offsetPt);    // Hide the cursor. 
  1246.     waitUntil = TickCount() + kTicksPerFrame;    // Set up speed governor variable.
  1247.     
  1248.     do                                            // Main game loop!!!!
  1249.     {    
  1250.         MovePlayer();                            // Move the player's bird.
  1251.         MoveEnemies();                            // Move all sphinx enemies.
  1252.         HandleHand();                            // Handle the mummy hand (may do nothing).
  1253.         HandleEye();                            // Handle eye (probably will do nothing).
  1254.         DrawFrame();                            // Draw the whole scene for this frame.
  1255.         HandleLightning();                        // Draw lightning (is applicable).
  1256.         do                                        // Here is where the speed is governed.
  1257.         {
  1258.         } while (TickCount() < waitUntil);
  1259.         waitUntil = TickCount() + kTicksPerFrame;
  1260.         evenFrame = !evenFrame;                    // Toggle "evenFrame" variable.
  1261.         
  1262.         GetPlayerInput();                        // Get the player's input (keystrokes).
  1263.         HandleCountDownTimer();                    // Handle countdown (may do nothing).
  1264.         FinishLightning();                        // Undraw lightning (if it needs undoing).
  1265.     }
  1266.     while ((playing) && (!pausing));            // Stay in loop until dead, paused or quit.
  1267.     
  1268.     if ((!playing) && (!quitting))                // If the player died!
  1269.     {                                            // Then play some sweet music.
  1270.         PlayExternalSound(kMusicSound, kMusicPriority);
  1271.         CheckHighScore();                        // And see if they're on the high scores.
  1272.     }
  1273.     
  1274.     ShowCursor();                                // Before we go, restore the cursor.
  1275.     MenusReflectMode();                            // Set the menus grayed-out state correctly.
  1276.     FlushEvents(everyEvent, 0);                    // Flush any events in the queue.
  1277. }
  1278.  
  1279. //--------------------------------------------------------------  CheckHighScore
  1280.  
  1281. // This function handles testing to see if the player's score is in the …
  1282. // high scores.  If that is the case, the function prompts the user for…
  1283. // a name to enter, and sorts and stores off the new score list.
  1284.  
  1285. void CheckHighScore (void)
  1286. {
  1287.     #define        kHighNameDialogID    130
  1288.     Str255        placeStr, tempStr;
  1289.     DialogPtr    theDial;
  1290.     short        i, item;
  1291.     Boolean        leaving;
  1292.     
  1293.     if (theScore > thePrefs.highScores[9])        // To see if on high scores, we need…
  1294.     {                                            // merely see if the last guy is beat out.
  1295.         openTheScores = TRUE;                    // Will automatically bring up high scores.
  1296.                                                 // Play some congratulatory music.
  1297.         PlayExternalSound(kBonusSound, kMusicPriority - 1);
  1298.         i = 8;                                    // Find where new score fits in list.
  1299.         while ((theScore > thePrefs.highScores[i]) && (i >= 0))
  1300.         {                                        // We'll bump everyone down as we look.
  1301.             thePrefs.highScores[i + 1] = thePrefs.highScores[i];
  1302.             thePrefs.highLevel[i + 1] = thePrefs.highLevel[i];
  1303.             PasStringCopy(thePrefs.highNames[i], thePrefs.highNames[i + 1]);
  1304.             i--;
  1305.         }
  1306.         
  1307.         i++;                                    // i is our place in list (zero based).
  1308.         thePrefs.highScores[i] = theScore;        // Pop the new score in place.
  1309.         thePrefs.highLevel[i] = levelOn + 1;    // Drop in the new highest level.
  1310.         
  1311.         NumToString((long)i + 1L, placeStr);    // Convert place to a string to display…
  1312.         ParamText(placeStr, "\p", "\p", "\p");    // in the dialog (via ParamText()).
  1313.         
  1314.         InitCursor();                            // Show cursor.
  1315.         CenterDialog(kHighNameDialogID);        // Center the dialog and then bring it up.
  1316.         theDial = GetNewDialog(kHighNameDialogID, 0L, kPutInFront);
  1317.         SetPort((GrafPtr)theDial);
  1318.         ShowWindow((GrafPtr)theDial);            // Make dialog visible.
  1319.         DrawDefaultButton(theDial);                // Draw outline around "Okay" button.
  1320.         FlushEvents(everyEvent, 0);                // Flush any events queued up.
  1321.                                                 // Put a default name in text edit box.
  1322.         SetDialogString(theDial, 2, thePrefs.highName);
  1323.         SelIText(theDial, 2, 0, 1024);            // Select the whole text edit string.
  1324.         leaving = FALSE;                        // Flag for noting when player hit "Okay".
  1325.         
  1326.         while (!leaving)                        // Simple modal dialog loop.
  1327.         {
  1328.             ModalDialog(0L, &item);                // Use standard filtering.
  1329.             
  1330.             if (item == 1)                        // If player hit the "Okay" button…
  1331.             {                                    // Get the name entered in text edit box.
  1332.                 GetDialogString(theDial, 2, tempStr);
  1333.                                                 // Copy the name into high score list.
  1334.                 PasStringCopyNum(tempStr, thePrefs.highNames[i], 15);
  1335.                 PasStringCopy(thePrefs.highNames[i], thePrefs.highName);
  1336.                 leaving = TRUE;                    // We're gone!
  1337.             }
  1338.         }
  1339.         
  1340.         DisposDialog(theDial);                    // Clean up.
  1341.     }
  1342.     else                                        // But if player didn't get on high scores…
  1343.         openTheScores = FALSE;                    // no need to rub their face in it.
  1344. }
  1345.  
  1346.